package com.kamikaze.docidset.impl;

import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.PriorityQueue;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.search.DocIdSetIterator;

public class OrDocIdSet extends DocIdSet implements Serializable
{
  private static final long serialVersionUID = 1L;
  
  private static transient Log log = LogFactory.getLog(OrDocIdSet.class);
  
  public class AescDocIdSetComparator implements Comparator<DocIdSetIterator>, Serializable
  {

    private static final long serialVersionUID = 1L;
    public int compare(DocIdSetIterator o1, DocIdSetIterator o2)
    {
        return o1.doc() - o2.doc(); 
    }

    
  }
  
   List<DocIdSet> sets = null;
   List<DocIdSetIterator> docSets = null;
   PriorityQueue<DocIdSetIterator> pq = null;
  
  public OrDocIdSet(List<DocIdSet> docSets)
  {
 	this.sets = docSets;
  }
  
  
  
  class OrDocIdSetIterator extends DocIdSetIterator
  {
    int lastReturn = -1;
    
    OrDocIdSetIterator() 
    {
        initialize();
    }

    private void initialize() 
    {
    	docSets = new ArrayList<DocIdSetIterator>();
    	for(Iterator<DocIdSet> ii = sets.iterator();ii.hasNext();)
    	{
    		docSets.add(ii.next().iterator());
    	}
    	
        if(docSets == null || docSets.size() < 1)
          throw new IllegalArgumentException("Minimum one iterator required");
        pq = new PriorityQueue<DocIdSetIterator>(docSets.size(), new AescDocIdSetComparator());
        for(int i=0;i<docSets.size();i++)
        {
          try
          {
            docSets.get(i).next();
            pq.offer(docSets.get(i));
          }
          catch (IOException e)
          {
            log.error(e.getMessage(),e);
          }
         
        }
        
    }

    @Override
    public int doc()
    {
      return lastReturn;
    }

    @Override
    public boolean next() throws IOException 
    {
    	
    	
        DocIdSetIterator dcit = pq.poll();
        if(dcit == null)
          return false;
        else
        {       
            while(pq.size()!=0)
            {
             if(lastReturn != dcit.doc())
             {    
                lastReturn = dcit.doc();
                
                  if(dcit.next())
                  {
                    pq.offer(dcit);
                  }
                  return true;
              }
              else
              {
                if(dcit!=null && dcit.next())
                {
                  pq.offer(dcit);
                  dcit = pq.poll();
                }
                
                else {
                	dcit = pq.poll();
                	if(dcit.doc()!=lastReturn && pq.size() == 0)
                	{
                		lastReturn = dcit.doc();
                		if(dcit.next())
                			pq.offer(dcit);
                		return true;
                	}
                }
              }
              
            }
            if(lastReturn != dcit.doc())
            {
            	lastReturn = dcit.doc();
            	if(pq.size() == 0 && dcit.next())
            		pq.offer(dcit);
            	return true;
            }
            while(pq.size() == 0 && dcit.next())
            {
            	if(dcit.doc()==lastReturn)
            		continue;
                    lastReturn = dcit.doc();
            		pq.offer(dcit);
            		return true;
            	
            }
            
            	
            return false;
        }
     
    }

    @Override
    public boolean skipTo(int target) throws IOException
    {
    
    	
    	DocIdSetIterator dcit = null; 
    	docSets.clear();
    	boolean success = false;
    	while(!pq.isEmpty())
    	{   
    		
    		dcit = pq.poll();
    		if(dcit.skipTo(target))
    		{
    			success = true;
    			docSets.add(dcit);
    		}
    	}
	   for(int i=0;i<docSets.size();i++)
       {
         pq.offer(docSets.get(i));
       }
	  
	   if(lastReturn == target)
		   return true;
	   else if(pq.isEmpty())
		   return false;
	   lastReturn = pq.peek().doc();
       docSets.clear();
       
       return success;
    }	
  }
  
  @Override
  public DocIdSetIterator iterator()
  {
      return new OrDocIdSetIterator();
  }

}
